#!/usr/bin/env python3
"""
音频处理模块 - FFmpeg版本
使用FFmpeg进行高质量音频处理，支持多种音频格式和效果
"""

import os
import asyncio
import logging
import subprocess
import tempfile
from typing import Optional, Dict, List
from pathlib import Path
import json

logger = logging.getLogger(__name__)


class FFmpegAudioProcessor:
    """基于FFmpeg的音频处理器"""
    
    def __init__(self):
        self.ffmpeg_available = self._check_ffmpeg()
        self.sample_rate = 44100
        self.channels = 2
        
        if not self.ffmpeg_available:
            logger.warning("FFmpeg未安装或不可用")
        else:
            logger.info("FFmpeg音频处理器初始化成功")
    
    def _check_ffmpeg(self) -> bool:
        """检查FFmpeg是否可用"""
        try:
            result = subprocess.run(
                ['ffmpeg', '-version'], 
                capture_output=True, 
                text=True, 
                timeout=10
            )
            return result.returncode == 0
        except (subprocess.SubprocessError, FileNotFoundError, TimeoutError):
            return False
    
    async def process_audio(
        self, 
        input_path: str, 
        output_path: str,
        effects_config: Dict = None
    ) -> bool:
        """
        处理音频文件，应用各种效果
        
        Args:
            input_path: 输入文件路径
            output_path: 输出文件路径
            effects_config: 效果配置字典
            
        Returns:
            处理是否成功
        """
        if not self.ffmpeg_available:
            logger.error("FFmpeg不可用，无法处理音频")
            return False
            
        if not effects_config:
            effects_config = {}
        
        try:
            cmd = ['ffmpeg', '-i', input_path, '-y']
            filters = []
            
            # 音量调整
            volume = effects_config.get('volume', 1.0)
            if volume != 1.0:
                filters.append(f"volume={volume}")
            
            # 音调调整
            pitch = effects_config.get('pitch', 1.0)
            if pitch != 1.0:
                # 使用asetrate+aresample来改变音调
                new_rate = int(self.sample_rate * pitch)
                filters.append(f"asetrate={new_rate},aresample={self.sample_rate}")
            
            # 速度调整
            speed = effects_config.get('speed', 1.0)
            if speed != 1.0:
                if 0.5 <= speed <= 2.0:  # FFmpeg atempo限制
                    filters.append(f"atempo={speed}")
                else:
                    # 对于极端速度，使用多个atempo滤镜
                    current_speed = speed
                    while current_speed > 2.0:
                        filters.append("atempo=2.0")
                        current_speed /= 2.0
                    while current_speed < 0.5:
                        filters.append("atempo=0.5")
                        current_speed *= 2.0
                    if current_speed != 1.0:
                        filters.append(f"atempo={current_speed}")
            
            # 回声效果
            if effects_config.get('echo', False):
                echo_delay = effects_config.get('echo_delay', 1000)
                echo_decay = effects_config.get('echo_decay', 0.3)
                filters.append(f"aecho=0.8:0.9:{echo_delay}:{echo_decay}")
            
            # 混响效果
            if effects_config.get('reverb', False):
                reverb_size = effects_config.get('reverb_size', 0.5)
                reverb_wet = effects_config.get('reverb_wet', 0.3)
                filters.append(f"afreqshift=shift=0,aecho=1.0:0.7:20:0.25")
            
            # 低通滤波器
            lowpass_freq = effects_config.get('lowpass')
            if lowpass_freq:
                filters.append(f"lowpass=f={lowpass_freq}")
            
            # 高通滤波器
            highpass_freq = effects_config.get('highpass')
            if highpass_freq:
                filters.append(f"highpass=f={highpass_freq}")
            
            # 带通滤波器
            if effects_config.get('bandpass'):
                bp_freq = effects_config['bandpass']
                bp_width = effects_config.get('bandpass_width', 1000)
                filters.append(f"bandpass=f={bp_freq}:width_type=h:w={bp_width}")
            
            # 压缩器
            if effects_config.get('compressor', False):
                threshold = effects_config.get('comp_threshold', -20)
                ratio = effects_config.get('comp_ratio', 4)
                attack = effects_config.get('comp_attack', 5)
                release = effects_config.get('comp_release', 50)
                filters.append(
                    f"acompressor=threshold={threshold}dB:ratio={ratio}:"
                    f"attack={attack}:release={release}"
                )
            
            # 噪音门
            if effects_config.get('gate', False):
                gate_threshold = effects_config.get('gate_threshold', -50)
                filters.append(f"agate=threshold={gate_threshold}dB")
            
            # 失真效果
            if effects_config.get('distortion', False):
                gain = effects_config.get('distortion_gain', 20)
                filters.append(f"overdrive=gain={gain}")
            
            # 合唱效果
            if effects_config.get('chorus', False):
                filters.append("chorus=0.7:0.9:55:0.4:0.25:2:2")
            
            # 镶边效果
            if effects_config.get('flanger', False):
                filters.append("flanger=delay=7:depth=2:regen=0.3:width=71")
            
            # 相位器
            if effects_config.get('phaser', False):
                filters.append("aphaser=in_gain=0.4:out_gain=0.74:delay=3:decay=0.4")
            
            # 颤音效果
            tremolo_freq = effects_config.get('tremolo_freq')
            if tremolo_freq:
                tremolo_depth = effects_config.get('tremolo_depth', 0.5)
                filters.append(f"tremolo=f={tremolo_freq}:d={tremolo_depth}")
            
            # 应用滤镜
            if filters:
                cmd.extend(['-af', ','.join(filters)])
            
            # 输出格式设置
            output_format = effects_config.get('format', 'mp3')
            bitrate = effects_config.get('bitrate', '128k')
            
            if output_format == 'mp3':
                cmd.extend([
                    '-acodec', 'libmp3lame',
                    '-b:a', bitrate,
                ])
            elif output_format == 'wav':
                cmd.extend([
                    '-acodec', 'pcm_s16le',
                ])
            elif output_format == 'aac':
                cmd.extend([
                    '-acodec', 'aac',
                    '-b:a', bitrate,
                ])
            
            # 采样率和声道设置
            cmd.extend([
                '-ar', str(self.sample_rate),
                '-ac', str(self.channels),
                output_path
            ])
            
            # 执行FFmpeg命令
            process = await asyncio.create_subprocess_exec(
                *cmd,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            
            stdout, stderr = await process.communicate()
            
            if process.returncode == 0:
                logger.info(f"音频处理成功: {output_path}")
                return True
            else:
                logger.error(f"FFmpeg处理失败: {stderr.decode()}")
                return False
                
        except Exception as e:
            logger.error(f"音频处理异常: {e}")
            return False
    
    async def convert_format(
        self, 
        input_path: str, 
        output_path: str,
        target_format: str = 'mp3',
        bitrate: str = '128k'
    ) -> bool:
        """转换音频格式"""
        if not self.ffmpeg_available:
            return False
        
        try:
            cmd = ['ffmpeg', '-i', input_path, '-y']
            
            if target_format == 'mp3':
                cmd.extend(['-acodec', 'libmp3lame', '-b:a', bitrate])
            elif target_format == 'wav':
                cmd.extend(['-acodec', 'pcm_s16le'])
            elif target_format == 'aac':
                cmd.extend(['-acodec', 'aac', '-b:a', bitrate])
            elif target_format == 'ogg':
                cmd.extend(['-acodec', 'libvorbis', '-b:a', bitrate])
            
            cmd.append(output_path)
            
            process = await asyncio.create_subprocess_exec(
                *cmd,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            
            stdout, stderr = await process.communicate()
            return process.returncode == 0
            
        except Exception as e:
            logger.error(f"格式转换失败: {e}")
            return False
    
    async def get_audio_info(self, audio_path: str) -> Optional[Dict]:
        """获取音频文件信息"""
        if not self.ffmpeg_available:
            return None
        
        try:
            cmd = [
                'ffprobe', '-v', 'quiet', '-print_format', 'json',
                '-show_format', '-show_streams', audio_path
            ]
            
            process = await asyncio.create_subprocess_exec(
                *cmd,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            
            stdout, stderr = await process.communicate()
            
            if process.returncode == 0:
                info = json.loads(stdout.decode())
                
                # 提取音频流信息
                audio_stream = None
                for stream in info.get('streams', []):
                    if stream.get('codec_type') == 'audio':
                        audio_stream = stream
                        break
                
                if audio_stream:
                    return {
                        'duration': float(info['format'].get('duration', 0)),
                        'bitrate': int(info['format'].get('bit_rate', 0)),
                        'sample_rate': int(audio_stream.get('sample_rate', 0)),
                        'channels': int(audio_stream.get('channels', 0)),
                        'codec': audio_stream.get('codec_name', ''),
                        'size': int(info['format'].get('size', 0))
                    }
            
            return None
            
        except Exception as e:
            logger.error(f"获取音频信息失败: {e}")
            return None
    
    async def trim_audio(
        self, 
        input_path: str, 
        output_path: str,
        start_time: float = 0,
        duration: Optional[float] = None
    ) -> bool:
        """裁剪音频"""
        if not self.ffmpeg_available:
            return False
        
        try:
            cmd = ['ffmpeg', '-i', input_path, '-y', '-ss', str(start_time)]
            
            if duration:
                cmd.extend(['-t', str(duration)])
            
            cmd.extend(['-acodec', 'copy', output_path])
            
            process = await asyncio.create_subprocess_exec(
                *cmd,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            
            stdout, stderr = await process.communicate()
            return process.returncode == 0
            
        except Exception as e:
            logger.error(f"音频裁剪失败: {e}")
            return False
    
    async def merge_audio(
        self, 
        input_paths: List[str], 
        output_path: str
    ) -> bool:
        """合并音频文件"""
        if not self.ffmpeg_available or len(input_paths) < 2:
            return False
        
        try:
            # 创建文件列表
            with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', 
                                           delete=False) as f:
                for path in input_paths:
                    f.write(f"file '{path}'\n")
                list_file = f.name
            
            try:
                cmd = [
                    'ffmpeg', '-f', 'concat', '-safe', '0',
                    '-i', list_file, '-y', '-acodec', 'copy', output_path
                ]
                
                process = await asyncio.create_subprocess_exec(
                    *cmd,
                    stdout=asyncio.subprocess.PIPE,
                    stderr=asyncio.subprocess.PIPE
                )
                
                stdout, stderr = await process.communicate()
                return process.returncode == 0
                
            finally:
                os.unlink(list_file)
                
        except Exception as e:
            logger.error(f"音频合并失败: {e}")
            return False
    
    def get_processing_info(self) -> Dict:
        """获取音频处理器信息"""
        return {
            "processor": "FFmpeg",
            "available": self.ffmpeg_available,
            "sample_rate": self.sample_rate,
            "channels": self.channels,
            "supported_formats": ["mp3", "wav", "aac", "ogg"],
            "supported_effects": [
                "volume", "pitch", "speed", "echo", "reverb",
                "lowpass", "highpass", "bandpass", "compressor",
                "gate", "distortion", "chorus", "flanger", 
                "phaser", "tremolo"
            ] if self.ffmpeg_available else []
        }


# 创建全局音频处理器实例
audio_processor = FFmpegAudioProcessor() 